package com.gitee.feizns.quickstart.web.spring.boot.autoconfigure.restful;

import com.gitee.feizns.quickstart.spring.AppUtils;
import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.lang.NonNull;
import org.springframework.util.ClassUtils;
import org.springframework.web.servlet.function.RouterFunction;
import org.springframework.web.servlet.function.ServerResponse;

import java.util.Set;

/**
 * restful api注册
 * @author feizns
 * @since 2023/07/11
 */
@DependsOn({"appUtils"})
@AutoConfigureBefore(WebMvcAutoConfiguration.class)
@AutoConfigureAfter(QuickstartRestfulAutoConfiguration.class)
public class RestfulApiAnnotationRegistrar implements ImportBeanDefinitionRegistrar,
        ResourceLoaderAware, BeanClassLoaderAware, BeanFactoryAware, ApplicationContextAware {

    /**
     * 类加载器
     */
    protected ClassLoader classLoader;

    /**
     * 资源加载器
     */
    protected ResourceLoader resourceLoader;

    @Override
    public void registerBeanDefinitions(@NonNull AnnotationMetadata importingClassMetadata, @NonNull BeanDefinitionRegistry registry) {
        //获取注解元数据包名
        String backPackage = ClassUtils.getPackageName(importingClassMetadata.getClassName());
        //类路径组件扫描器
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
        //设置资源加载器
        provider.setResourceLoader(this.resourceLoader);
        //查找包含指定注解类型的过滤器
        provider.addIncludeFilter(new AnnotationTypeFilter(RestApi.class));
        //查找符合条件的bean定义
        Set<BeanDefinition> beanDefinitions = provider.findCandidateComponents(backPackage);
        //重置过滤器
        provider.resetFilters(false);
        //bean定义处理
        handle(beanDefinitions);
    }

    /**
     * 处理
     * @param restfulApis RESTful API
     */
    @SneakyThrows
    public void handle(Set<BeanDefinition> restfulApis) {
        for (BeanDefinition api : restfulApis) {
            //加载类型
            Class<?> apiBeanClass = this.classLoader.loadClass(api.getBeanClassName());
            //beanName
            String beanName = "RestApiBean:" + apiBeanClass.getName();
            //创建函数路由
            RouterFunction<ServerResponse> function = RestfulRouterFunctionCreator.route(apiBeanClass).build();
            //注册到容器中
            AppUtils.registry(beanName, function);
        }
    }

    @Override
    public void setBeanClassLoader(@NonNull ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @Override
    public void setResourceLoader(@NonNull ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException {
        AppUtils.staticSetBeanFactory(beanFactory);
    }

    @Override
    public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
        AppUtils.staticSetApplicationContext(applicationContext);
    }

}
